package com.thebitstream.sandy.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;

import com.thebitstream.sandy.support.ClientData;

/**
 * @author Andy Shaules
 *
 */
public class Engine extends Thread {

	public static final String ID = "engine.id";

	public static final String DATA = "engine.data";

	private Map<ClientData, Notify> pendingNotify = new HashMap<ClientData, Notify>();

	private IScope _scope;

	private boolean _roomIsAlive = true;

	public static long granularity = 100;
	public static long timeout = 60000;
	public Engine(IScope scope) {
		_scope = scope;
	}

	public void kill() {
		_roomIsAlive = false;
	}

	@Override
	public void run() {

		while (_roomIsAlive) {
			
			//Here is where you could do server-side computation on the client data.
			// processData();

			ArrayList<ClientData> data = getScopeData();

			try {

				Collection<Set<IConnection>> clients = _scope.getConnections();

				for (Set<IConnection> connectionSet : clients) {
					for (final IConnection connection : connectionSet) {
						makeNotify(connection, data);
					}
				}

				sleep(granularity);
			} catch (Exception e) {

			}
		}
	}

	/**
	 * Here is where you could do server-side computation on the client data.
	 */
	@SuppressWarnings("unused")
	private void processData() {
		//		ArrayList<ClientData> data = getScopeData();
		//
		//		for (int j = 0; j < data.size(); j++) {
		//			// client position
		//			double[] temp = ((ClientData) data.get(j)).getPosition();
		//		}
	}

	public synchronized void removeClient(ClientData object) {
		pendingNotify.remove(object);

	}

	public synchronized void onCallBack(ClientData object, ArrayList<Object> object2) {
		pendingNotify.remove(object);
		object.readReturn(object2);
	}

	private synchronized void makeNotify(IConnection client, ArrayList<ClientData> data2) {
		// currently waiting for last packet acknowledgment?
		if (pendingNotify.containsKey((ClientData) client.getClient().getAttribute(DATA))) {
			Object[] headerCapsule = pendingNotify.get((ClientData) client.getClient().getAttribute(DATA)).getHeader();
			//60 second time out on packet acknowledgments.
			if (System.currentTimeMillis() - Long.valueOf(headerCapsule[2].toString()) >= timeout) {
				//timed out, try again.
				pendingNotify.remove((ClientData) client.getClient().getAttribute(DATA));
			} else {
				// Still waiting for last message to return.
				return;
			}
		}
		Object[] dists = new Object[] { ((ClientData) client.getClient().getAttribute(DATA)).data };
		Object[] capsuleHeader = new Object[data2.size() + 1];
		capsuleHeader[0] = new Object[] { client.getClient().getAttribute(ID), dists, System.currentTimeMillis() };
		for (int i = 1; (i) < capsuleHeader.length; i++) {
			capsuleHeader[i] = data2.get(i - 1).toObject();
		}

		ClientData cd = (ClientData) client.getClient().getAttribute(DATA);
		Notify notify = new Notify(client, capsuleHeader, this, cd);
		pendingNotify.put(cd, notify);
		notify.start();
	}

	private ArrayList<ClientData> getScopeData() {
		ArrayList<ClientData> ret = new ArrayList<ClientData>();

		Collection<Set<IConnection>> clients = _scope.getConnections();

		for (Set<IConnection> connectionSet : clients) {
			for (final IConnection connection : connectionSet) {
				ClientData clientData = ((ClientData) connection.getClient().getAttribute(DATA, "engine.data"));
				if(clientData!= null)
				ret.add(clientData);
			}
		}

		return ret;
	}
}
